home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
src
/
haeberli
/
libgutil
/
vcool.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
11KB
|
369 lines
/*
* vcool -
* Scan an image for pixels with RGB values that will give
* "unsafe" values of chrominance signal or composite signal
* amplitude when encoded into an NTSC or PAL colour signal. This
* happens for certain high-intensity high-saturation colours that
* are rare in real scenes, but can easily be present in synthetic
* images.
*
* Such pixels can be flagged so the user may then choose other
* colours. Or, the offending pixels can be made "safe"
* in a manner that preserves hue.
*
* There are two reasonable ways to make a pixel "safe":
* We can reduce its intensity (luminance) while leaving
* hue and saturation the same. Or, we can reduce saturation
* while leaving hue and luminance the same. An argument selects
* which strategy to use.
*
* exports:
* int vcool(short *red, short *green, short *blue, int n);
* void vcoolpixrepair(repair);
* void vcoolpixgamma(gam);
*
* <>
*
* Originally written as "ikNTSC.c" by Alan Wm Paeth,
* University of Waterloo, August, 1985
*
* Previous incarnation was hot.c by Dave Martindale,
* Imax Systems Corp., December 1990
*
* Updated by Dave Martindale, Imax Systems Corp., December 1990
*
* Mucked with by David Blythe, Silicon Graphics, December 1991
*
* <>
* Compile-time options.
*
* DEFPIXGAMMA
* Default gamma of the input and output pixels. This value is 1.7
* for GL applications typically. This should really be obtained
* from the graphics system dynamically.
*
* NTSC and PAL
* Define either NTSC or PAL as 1 to select the colour system.
* Define the other one as zero, or leave it undefined.
*
* CHROMA_LIM
* is the limit (in IRE units) of the overall chrominance amplitude;
* it should be 50 or perhaps very slightly higher.
*
* COMPOS_LIM
* is the maximum amplitude (in IRE units) allowed for the composite
* signal. A value of 100 is the maximum monochrome white, and is
* always safe. 120 is the absolute limit for NTSC broadcasting,
* since the transmitter's carrier goes to zero with 120 IRE input
* signal. Generally, 110 is a good compromise - it allows somewhat
* brighter colours than 100, while staying safely away from the
* hard limit.
*/
#include "math.h"
#include "stdio.h"
#include "vcool.h"
#define DEFPIXGAMMA (1.7) /* this is the default gamma on IRIS */
#define NTSC (1)
#define PAL (0)
#define CHROMA_LIM (50.0) /* chroma amplitude limit */
#define COMPOS_LIM (110.0) /* max IRE amplitude */
#if NTSC
/*
* RGB to YIQ encoding matrix.
*/
static float code_matrix[3][3] = {
0.2989, 0.5866, 0.1144,
0.5959, -0.2741, -0.3218,
0.2113, -0.5227, 0.3113,
};
#define PEDESTAL 7.5 /* 7.5 IRE black pedestal */
#define VIDGAMMA 2.2
#endif /* NTSC */
#if PAL
/*
* RGB to YUV encoding matrix.
*/
static float code_matrix[3][3] = {
0.2989, 0.5866, 0.1144,
-0.1473, -0.2891, 0.4364,
0.6149, -0.5145, -0.1004,
};
#define PEDESTAL 0.0 /* no pedestal in PAL */
#define VIDGAMMA 2.8
#endif /* PAL */
#define SCALE 8192 /* scale factor: do floats with int math */
#define MAXPIX 255 /* white value */
/*
* if input pixels are in linear space #define CORGAMMA (VIDGAMMA)
* if input pixels are in video space #define CORGAMMA (1.0)
*/
#define CORGAMMA (VIDGAMMA*(defpixgamma/2.2))
/*
* pixel decoding and encoding functions, map integer pixel value
* to [0,1] and vice versa. For now assume a simple linear transformation
*/
#define pix_decode(v) (((float)(v))/MAXPIX)
#define pix_encode(v) ((int)((v)*MAXPIX+.5))
/*
* gc: apply the gamma correction specified for this video standard.
* inv_gc: inverse function of gc.
*
* These are generally just a call to powf(), but be careful!
* Future standards may use more complex functions.
* (e.g. SMPTE 240M's "electro-optic transfer characteristic").
*/
#define gc(x) powf((x), (float)1.0 / (float)CORGAMMA)
#define inv_gc(x) powf((x), (float)CORGAMMA)
static int tab[3][3][MAXPIX+1]; /* multiply lookup table */
static float chroma_lim; /* chroma limit */
static float compos_lim; /* composite amplitude limit */
static long ichroma_lim2; /* chroma limit squared (scaled integer) */
static int icompos_lim; /* composite amplitude limit (scaled integer) */
static float defpixgamma; /* the default gamma ramp is 1.7 */
static int tabmade = 0;
static int repairmode = 0;
/*
* build_tab: Build multiply lookup table.
*
* For each possible pixel value, decode value into floating-point
* intensity. Then do gamma correction required by the video
* standard. Scale the result by our fixed-point scale factor.
* Then calculate 9 lookup table entries for this pixel value.
*
* We also calculate floating-point and scaled integer versions
* of our limits here. This prevents evaluating expressions every pixel
* when the compiler is too stupid to evaluate constant-valued
* floating-point expressions at compile time.
*
* For convenience, the limits are #defined using IRE units.
* We must convert them here into the units in which YIQ
* are measured. The conversion from IRE to internal units
* depends on the pedestal level in use, since as Y goes from
* 0 to 1, the signal goes from the pedestal level to 100 IRE.
* Chroma is always scaled to remain consistent with Y.
*/
static build_tab()
{
float f;
int pv;
for (pv = 0; pv <= MAXPIX; pv++) {
f = SCALE * gc(pix_decode(pv));
tab[0][0][pv] = (int)(f * code_matrix[0][0] + 0.5);
tab[0][1][pv] = (int)(f * code_matrix[0][1] + 0.5);
tab[0][2][pv] = (int)(f * code_matrix[0][2] + 0.5);
tab[1][0][pv] = (int)(f * code_matrix[1][0] + 0.5);
tab[1][1][pv] = (int)(f * code_matrix[1][1] + 0.5);
tab[1][2][pv] = (int)(f * code_matrix[1][2] + 0.5);
tab[2][0][pv] = (int)(f * code_matrix[2][0] + 0.5);
tab[2][1][pv] = (int)(f * code_matrix[2][1] + 0.5);
tab[2][2][pv] = (int)(f * code_matrix[2][2] + 0.5);
}
chroma_lim = (float)CHROMA_LIM / (100.0 - PEDESTAL);
compos_lim = ((float)COMPOS_LIM - PEDESTAL) / (100.0 - PEDESTAL);
ichroma_lim2 = (int)(chroma_lim * SCALE + 0.5);
ichroma_lim2 *= ichroma_lim2;
icompos_lim = (int)(compos_lim * SCALE + 0.5);
}
void vcoolpixgamma(gam)
float gam;
{
defpixgamma = gam;
build_tab();
tabmade++;
}
void vcoolrepairmode(repair)
int repair;
{
repairmode = repair;
}
int vcool(short *red, short *green, short *blue, int n)
{
int r, g, b;
int y, i, q;
long y2, c2;
float pr, pg, pb;
float py;
float fy, fc, t, scale;
static int prev_r = 0, prev_g = 0, prev_b = 0;
static int new_r, new_g, new_b;
int nhot = 0;
if (!tabmade)
vcoolpixgamma(DEFPIXGAMMA);
if (!repairmode)
vcoolrepairmode(REDUCE_SAT);
while(n--) {
r = *red++;
g = *green++;
b = *blue++;
/*
* Pixel decoding, gamma correction, and matrix multiplication
* all done by lookup table.
*
* "i" and "q" are the two chrominance components;
* they are I and Q for NTSC.
* For PAL, "i" is U (scaled B-Y) and "q" is V (scaled R-Y).
* Since we only care about the length of the chroma vector,
* not its angle, we don't care which is which.
*/
y = tab[0][0][r] + tab[0][1][g] + tab[0][2][b];
i = tab[1][0][r] + tab[1][1][g] + tab[1][2][b];
q = tab[2][0][r] + tab[2][1][g] + tab[2][2][b];
/*
* Check to see if the chrominance vector is too long or the
* composite waveform amplitude is too large.
*
* Chrominance is too large if
*
* sqrt(i^2, q^2) > chroma_lim.
*
* The composite signal amplitude is too large if
*
* y + sqrt(i^2, q^2) > compos_lim.
*
* We avoid doing the sqrt by checking
*
* i^2 + q^2 > chroma_lim^2
* and
* y + sqrt(i^2 + q^2) > compos_lim
* sqrt(i^2 + q^2) > compos_lim - y
* i^2 + q^2 > (compos_lim - y)^2
*
*/
c2 = (long)i * i + (long)q * q;
y2 = (long)icompos_lim - y;
y2 *= y2;
if (c2 <= ichroma_lim2 && c2 <= y2) /* no problems */
continue;
/*
* Pixel is hot, choose desired strategy
*/
if (repairmode == FLAG_HOT) {
/*
* Set the hot pixel to black to identify it.
*/
r = g = b = 0;
} else {
/*
* Optimization: cache the last-computed hot pixel.
*/
if (r == prev_r && g == prev_g && b == prev_b) {
r = new_r;
g = new_g;
b = new_b;
goto update;
}
prev_r = r;
prev_g = g;
prev_b = b;
/*
* Get Y and chroma amplitudes in floating point.
*
* If your C library doesn't have fhypot(), just use
* fhypot(a,b) = sqrt(a*a + b*b);
*
* Then extract linear (un-gamma-corrected) floating-point
* pixel RGB values.
*/
fy = (float)y / SCALE;
fc = fhypot((float)i / (float)SCALE, (float)q / (float)SCALE);
pr = pix_decode(r);
pg = pix_decode(g);
pb = pix_decode(b);
/*
* Reducing overall pixel intensity by scaling
* R, G, and B reduces Y, I, and Q by the same factor.
* This changes luminance but not saturation, since saturation
* is determined by the chroma/luminance ratio.
*
* On the other hand, by linearly interpolating between the
* original pixel value and a grey pixel with the same
* luminance (R=G=B=Y), we change saturation without
* affecting luminance.
*/
if (repairmode != REDUCE_SAT) {
/*
* Calculate a scale factor that will bring the pixel
* within both chroma and composite limits, if we scale
* luminance and chroma simultaneously.
*
* The calculated chrominance reduction applies to the
* gamma-corrected RGB values that are the input to
* the RGB-to-YIQ operation. Multiplying the
* original un-gamma-corrected pixel values by
* the scaling factor raised to the "gamma" power
* is equivalent, and avoids calling gc() and inv_gc()
* three times each.
*/
scale = chroma_lim / fc;
t = compos_lim / (fy + fc);
if (t < scale)
scale = t;
scale = powf(scale, (float)CORGAMMA);
r = pix_encode(scale * pr);
g = pix_encode(scale * pg);
b = pix_encode(scale * pb);
} else {
/*
* Calculate a scale factor that will bring the pixel
* within both chroma and composite limits, if we scale
* chroma while leaving luminance unchanged.
*
* We have to interpolate gamma-corrected RGB values,
* so we must convert from linear to gamma-corrected
* before interpolation and then back to linear afterwards.
*/
scale = chroma_lim / fc;
t = (compos_lim - fy) / fc;
if (t < scale)
scale = t;
pr = gc(pr);
pg = gc(pg);
pb = gc(pb);
py = pr * code_matrix[0][0] + pg * code_matrix[0][1]
+ pb * code_matrix[0][2];
r = pix_encode(inv_gc(py + scale * (pr - py)));
g = pix_encode(inv_gc(py + scale * (pg - py)));
b = pix_encode(inv_gc(py + scale * (pb - py)));
}
new_r = r;
new_g = g;
new_b = b;
}
update:
red[-1] = r;
green[-1] = g;
blue[-1] = b;
nhot++;
}
return nhot;
}